package com.flute.framework.search.result.xmlresult;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import com.flute.framework.search.SearchRequest;
import com.flute.framework.search.result.AbstractResultParser;
import com.flute.framework.search.result.MarkedResultProperty;
import com.flute.framework.search.result.ResultInfo;
import com.flute.framework.search.result.ResultProperty;
import com.flute.framework.search.result.SearchResult;
import com.flute.framework.search.result.xmlresult.multi.IXpathExParseEnable;
import com.flute.framework.search.result.xmlresult.multi.XpathExDistributer;
import com.flute.framework.search.result.xmlresult.multi.XpathExParserNotFoundException;
import com.flute.framework.util.XPathUtils;
import com.flute.tools.data.DataRecord;

public class XMLResultParser extends AbstractResultParser {
	private String beanPath;
	private static XpathExDistributer distributer = XpathExDistributer
			.getAllRegisterDistributer();
	private static Map<String, XmlResultTemplate> cache = new HashMap<String, XmlResultTemplate>();

	private static String getValueByXpath(Document doc, String xpath) {
		if (XPathUtils.isAttribute(xpath)) {
			return XPathUtils.getFirstAttributeValue(doc, xpath);
		} else {
			return XPathUtils.getFirstNodeValue(doc, xpath);
		}
	}

	private static String[] getValuesByXpath(Document doc, String xpath) {
		if (XPathUtils.isAttribute(xpath)) {
			return XPathUtils.getAttributeValues(doc, xpath);
		} else {
			return XPathUtils.getNodeValues(doc, xpath);
		}
	}

	private DataRecord parseResultProperty(Document doc) throws JDOMException {
		DataRecord record = null;
		if (resultProperties != null) {
			record = new DataRecord();
			for (int i = 0; i < resultProperties.size(); i++) {
				ResultProperty resultProperty = resultProperties.get(i);
				String name = resultProperty.getName();
				String xpath = resultProperty.getValue();
				String value = getValueByXpath(doc, xpath);
				record.AddField(name, value);
			}
		}
		return record;
	}

	private List<ResultInfo> parseToRequestInfo(int size,
			Map<String, String[]> valueMap) {
		List<ResultInfo> result = new ArrayList<ResultInfo>(size);
		for (int j = 0; j < size; j++) {
			ResultInfo info = new ResultInfo();
			for (Iterator<Entry<String, String[]>> iter = valueMap.entrySet()
					.iterator(); iter.hasNext();) {
				Entry<String, String[]> entry = iter.next();
				String name = entry.getKey();
				String[] values = entry.getValue();
				info.addProperty(name, values.length > j ? values[j] : null);
			}
			result.add(info);
		}
		return result;
	}

	private List<ResultInfo> parseResultInfo(Document doc) {
		Map<String, String[]> valueMap = new HashMap<String, String[]>();
		int max = 0;
		for (int i = 0; i < markedProperties.size(); i++) {
			MarkedResultProperty markedProperty = markedProperties.get(i);
			String name = markedProperty.getName();
			String xpath = markedProperty.getValue();
			String[] value = getValuesByXpath(doc, xpath);
			max = Math.max(max, value.length);
			valueMap.put(name, value);
		}
		return parseToRequestInfo(max, valueMap);
	}

	private SearchResult unParse(Document root) throws JDOMException {
		SearchResult result = new SearchResult();
		DataRecord dataRecord = parseResultProperty(root);
		List<ResultInfo> resultInfoList = parseResultInfo(root);
		result.setInfoList(resultInfoList);
		result.setPropertyList(dataRecord);
		return result;
	}

	private Node buildPro() {
		List<String> proXpathList = new ArrayList<String>(resultProperties
				.size() + 1);
		List<String> proValueList = new ArrayList<String>(resultProperties
				.size() + 1);
		proXpathList.add(beanPath);
		proValueList.add(null);
		for (ResultProperty curr : resultProperties) {
			proXpathList.add(curr.getValue());
			proValueList.add("$" + curr.getName());
		}
		return XmlParser.getInstance().getNode(
				proXpathList.toArray(new String[0]), null,
				proValueList.toArray(new String[0]));

	}

	private XmlResultTemplate buildTemplate()
			throws XpathExParserNotFoundException {
		// build template
		String head = XmlParser.getInstance().getHead("UTF-8", "1.0");
		XmlResultTemplate template = XmlResultTemplate.parse(head);
		Node proNode = buildPro();
		String name = beanPath.substring(beanPath.lastIndexOf("/") + 1);
		String[] strs = proNode.toXmlString().split("<" + name + "/>");
		if (strs != null && strs.length == 2) {
			template.setMain(strs[0]);
			template.setFoot(strs[1]);
		}
		List<String> xpathList = new ArrayList<String>(markedProperties.size());
		List<String> valueList = new ArrayList<String>(markedProperties.size());
		Map<String, ExXpathTemplate> temMap = new HashMap<String, ExXpathTemplate>();
		for (MarkedResultProperty pinfo : markedProperties) {
			valueList.add("$" + pinfo.getName());
			IXpathExParseEnable parser = distributer.distribute(pinfo
					.getValue());
			ExXpathTemplate exTemplate = parser.build(pinfo.getValue());
			if (exTemplate != null) {
				temMap.put(pinfo.getName(), exTemplate);
			}
			String baseXpath = parser.getBaseXpath(pinfo.getValue());
			xpathList.add(baseXpath);
		}
		// loop template
		Node root = XmlParser.getInstance().getNode(
				xpathList.toArray(new String[0]), null,
				valueList.toArray(new String[0]));
		String loop = root.getFirstNodeByXpath(beanPath).toXmlString();
		template.setLoop(loop);
		template.setExMap(temMap);
		return template;
	}

	private String parseByTemplate(XmlResultTemplate template,
			SearchRequest request, SearchResult result) {
		StringBuilder sb = new StringBuilder(1000);
		sb.append(template.getHead());
		String main = template.getMain();
		String[] properties = result.getAllProperties();
		for (String property : properties) {
			String value = result.getPropertyValue(property);
			main = main.replaceAll("\\$" + property, value);
		}
		sb.append(main);
		List<ResultInfo> recordList = result.getInfoList();
		for (ResultInfo info : recordList) {
			String loop = template.getLoop();
			String[] pros = info.getAllProperties();
			for (int i = 0; i < pros.length; i++) {
				MarkedResultProperty mark = markedProperties.size() > i ? markedProperties
						.get(i)
						: null;
				String pro = pros[i];
				String value = info.getPropertyValue(pro);
				ExXpathTemplate t = template.getExMap().get(pro);
				if (t != null) {
					StringBuilder sb1 = new StringBuilder("");
					String[] values = value.split(t.getSplit());
					for (String v : values) {
						if (mark != null && mark.getMarker() != null) {
							v = mark.getMarker().getMarkString(
									request.getKeyword(), v);
						}
						sb1.append(t.getLoop().replaceAll("\\$value", v));
					}
					value = sb1.toString();
					loop = loop.replaceAll("\\<!\\[CDATA\\[\\$" + pro
							+ "\\]\\]>", value);
				} else {
					if (mark != null && mark.getMarker() != null) {
						value = mark.getMarker().getMarkString(
								request.getKeyword(), value);
					}
					loop = loop.replaceAll("\\$" + pro, value);
				}

			}
			sb.append(loop);
		}
		String foot = template.getFoot();
		for (String property : properties) {
			String value = result.getPropertyValue(property);
			foot = foot.replaceAll("\\$" + property, value);
		}
		sb.append(foot);
		return sb.toString();
	}

	public String getBeanPagetNodeValuesth() {
		return beanPath;
	}

	public void setBeanPath(String beanPath) {
		this.beanPath = beanPath;
	}

	public SearchResult unParse(String xml) throws JDOMException, IOException {
		SAXBuilder sb = new SAXBuilder();
		Document doc = sb.build(new StringReader(xml));
		return unParse(doc);
	}

	public SearchResult unParse(InputStream xmlIs) throws JDOMException,
			IOException {
		SAXBuilder sb = new SAXBuilder();
		Document doc = sb.build(xmlIs);
		return unParse(doc);
	}

	public SearchResult unParse(Reader xmlReader) throws JDOMException,
			IOException {
		SAXBuilder sb = new SAXBuilder();
		Document doc = sb.build(xmlReader);
		return unParse(doc);
	}

	@Override
	public String parse(SearchRequest request, SearchResult result) {
		String searchName = request.getSearcherName();
		XmlResultTemplate template = cache.get(searchName);
		if (template == null) {
			try {
				template = buildTemplate();
				cache.put(searchName, template);
			} catch (XpathExParserNotFoundException e) {
				return null;
			}
		}
		return parseByTemplate(template, request, result);
	}

	public static void main(String[] args) throws FileNotFoundException,
			JDOMException, IOException {
		XMLResultParser parser = new XMLResultParser();
		List<ResultProperty> reList = new ArrayList<ResultProperty>();
		// time
		ResultProperty time = new ResultProperty();
		time.setName("time");
		time.setValue("//result/@time");
		// length
		ResultProperty length = new ResultProperty();
		length.setName("length");
		length.setValue("//result/@length");
		reList.add(time);
		reList.add(length);
		List<MarkedResultProperty> proList = new ArrayList<MarkedResultProperty>();
		// name
		MarkedResultProperty name = new MarkedResultProperty();
		name.setName("name");
		name.setValue("//result/songs/song/name");
		proList.add(name);
		parser.setResultProperties(reList);
		parser.setMarkedProperties(proList);
		SearchResult searchResult = parser
				.unParse("<result length=\"12\" time=\"11\"><songs><song><name>我要去西藏</name></song><song><name>我要去新疆</name></song><song><name>我要去桂林</name></song><song><name>我要去，我也不知道去哪</name></song></songs></result>");
		// show result property
		System.out
				.println("============= result property =======================================\n");
		String[] names = searchResult.getAllProperties();
		for (String currname : names) {
			System.out.println(currname + ":"
					+ searchResult.getPropertyValue(currname));
		}
		// show
		System.out
				.println("============= resultInfo =======================================\n");
		List<ResultInfo> infoList = searchResult.getInfoList();
		String[] properties = infoList.get(0).getAllProperties();
		for (String proper : properties) {
			System.out.print(proper + "\t");
		}
		System.out.println("\n");
		for (ResultInfo info : infoList) {
			for (String p : properties) {
				System.out.print(info.getPropertyValue(p) + "\t");
			}
			System.out.println("\n");
		}
	}
}
